#include "ring.h"

// ##
osMutexDef(ring_head);
osMutexDef(ring_tail);

// ##
#define ring_head_to_end(HANDLE)            (HANDLE->size - HANDLE->head)

// ##
#define ring_tail_to_end(HANDLE)            (HANDLE->size - HANDLE->tail)

// ##
uint16_t ring_unwrite_bytes(ringHandle *ring)
{
    if(ring->saveFlag == 1)
    {
        return 0;
    }
    else
    {
        if(ring->tail >= ring->head)
        {
            return (ring->size - ring->tail + ring->head);
        }
        else
        {
            return (ring->head - ring->tail);
        }
    }
}

// ##
uint16_t ring_unread_bytes(ringHandle *ring)
{
    if(ring->tail > ring->head)
    {
        return (ring->tail - ring->head);
    }
    else if(ring->tail < ring->head)
    {
        return (ring->size - ring->head + ring->tail);
    }
    else
    {
        if(ring->saveFlag == 1)
        {
            return ring->size;
        }
        else
        {
            return 0;
        }
    }
}

// ##
void ring_print_info(ringHandle *ring)
{
    // printk(" capacity-%d byte  usage-%d%%%%  used-%d byte  unused-%d byte\r\n", \
    //     ring->size, ring_unread_bytes(ring) * 100 / ring->size, ring_unread_bytes(ring), ring_unwrite_bytes(ring));
}

// ##
static void ring_set_head(ringHandle *ring, uint16_t newHead)
{
    if(ring->head == ring->tail)
    {
        ring->saveFlag = 0;
    }

    ring->head = newHead % ring->size;
}

// ##
static void ring_set_tail(ringHandle *ring, uint16_t newTail)
{
    if(ring->saveFlag == 0)
    {
        ring->tail = newTail % ring->size;
    }

    if(ring->head == ring->tail)
    {
        ring->saveFlag = 1;
    }
}

/*
    create and initialize a new ring
*/
ringHandle *ring_create(ringType *newRing, uint8_t *newBuffer, uint16_t buf_len)
{
    if(newRing == NULL || newBuffer == NULL || buf_len == 0)
    {
        return (ringHandle *)NULL;
    }
    else
    {
        newRing->head = 0;
        newRing->tail = 0;
        newRing->saveFlag = 0;
        newRing->size = buf_len;
        newRing->buf = newBuffer;

        newRing->head_mutex = osMutexCreate(osMutex(ring_head));
        newRing->tail_mutex = osMutexCreate(osMutex(ring_head));

        return (ringHandle *)newRing;
    }
}

/*
    add one byte into ringbuffer
    return : real bytes added into ringbuffer
*/
uint16_t ring_add_single_byte(ringHandle *ring, const uint8_t newByte)
{
    uint16_t return_size;

    if(ring == NULL)
    {
        return 0;
    }

    // ##
    if(osMutexWait(ring->tail_mutex, osWaitForever) == osOK)
    {
        // ##
        if(ring_unwrite_bytes(ring) > 0)
        {
            ring->buf[ring->tail] = newByte;
            ring_set_tail(ring, ring->tail + sizeof(uint8_t));

            return_size = sizeof(uint8_t);
        }
        else
        {
            return_size = 0;
        }

        // ##
        osMutexRelease(ring->tail_mutex);
    }

    return return_size;
}

/*
    add bytes into ringbuffer
    return : real bytes added into ringbuffer
*/
uint16_t ring_add_bytes(ringHandle *ring, const uint8_t *buf, uint16_t size)
{
    uint16_t left_size = 0;
    uint16_t return_size = 0;

    if(ring == NULL && size == 0)
    {
        return 0;
    }

    // ##
    if(osMutexWait(ring->tail_mutex, osWaitForever) == osOK)
    {
        // ##
        left_size = ring_unwrite_bytes(ring);

        if(ring->tail >= ring->head)
        {
            if(left_size >= size)
            {
                if(ring_tail_to_end(ring) >= size)
                {
                    memcpy(&ring->buf[ring->tail], buf, size);
                    ring_set_tail(ring, ring->tail + size);

                    return_size = size;
                }
                else
                {
                    if(ring_tail_to_end(ring) > 0)
                    {
                        memcpy(&ring->buf[ring->tail], buf, ring_tail_to_end(ring));
                        ring_set_tail(ring, 0);
                    }

                    memcpy(&ring->buf[ring->tail], &buf[ring_tail_to_end(ring)], size - ring_tail_to_end(ring));
                    ring_set_tail(ring, size - ring_tail_to_end(ring));

                    return_size = size;
                }
            }
            else
            {
                return_size = 0;
            }
        }
        else
        {
            if(left_size >= size)
            {
                memcpy(&ring->buf[ring->tail], buf, size);
                ring_set_tail(ring, ring->tail + size);

                return_size = size;
            }
            else
            {
                return_size = 0;
            }
        }

        // ##
        osMutexRelease(ring->tail_mutex);
    }

    return return_size;
}

/*
    fetch one byte from ringbuffer
    return : real bytes fetched from ringbuffer
*/
uint16_t ring_fetch_single_byte(ringHandle *ring, uint8_t *outByte)
{
    uint16_t return_size = 0;

    if(ring == NULL)
    {
        return 0;
    }

    // ##
    if(osMutexWait(ring->head_mutex, osWaitForever) == osOK)
    {
        // ##
        if(ring_unread_bytes(ring) > 0)
        {
            *outByte = ring->buf[ring->head];
            ring_set_head(ring, ring->head + sizeof(uint8_t));

            return_size = sizeof(uint8_t);
        }
        else
        {
            return_size = 0;
        }

        // ##
        osMutexRelease(ring->head_mutex);
    }

    return return_size;
}

/*
    fetch bytes from ringbuffer
    return : real bytes fetched from ringbuffer
*/
uint16_t ring_fetch_bytes(ringHandle *ring, uint8_t *buf, uint16_t size)
{
    uint16_t left_size = 0;
    uint16_t return_size = 0;

    if(ring == NULL && size == 0)
    {
        return 0;
    }

    // ##
    if(osMutexWait(ring->head_mutex, osWaitForever) == osOK)
    {
        // ##
        left_size = ring_unread_bytes(ring);

        if(ring->tail > ring->head)
        {
            if(left_size >= size)
            {
                memcpy(buf, &ring->buf[ring->head], size);
                ring_set_head(ring, ring->head + size);

                return_size = size;
            }
            else
            {
                memcpy(buf, &ring->buf[ring->head], left_size);
                ring_set_head(ring, ring->tail);

                return_size = left_size;
            }
        }
        else if(ring->tail <= ring->head)
        {
            if(left_size >= size)
            {
                if(ring_head_to_end(ring) >= size)
                {
                    memcpy(buf, &ring->buf[ring->head], size);
                    ring_set_head(ring, ring->head + size);

                    return_size = size;
                }
                else
                {
                    if(ring_head_to_end(ring) > 0)
                    {
                        memcpy(buf, &ring->buf[ring->head], ring_head_to_end(ring));
                        ring_set_head(ring, 0);
                    }

                    memcpy(&buf[ring_head_to_end(ring)], &ring->buf[ring->head], size - ring_head_to_end(ring));
                    ring_set_head(ring, size - ring_head_to_end(ring));

                    return_size = size;
                }
            }
            else
            {
                memcpy(buf, &ring->buf[ring->head], ring_head_to_end(ring));
                ring_set_head(ring, 0);

                memcpy(&buf[ring_head_to_end(ring)], &ring->buf[ring->head], ring->tail);
                ring_set_head(ring, ring->tail);

                return_size = left_size;
            }
        }
        else
        {
            return_size = 0;
        }

        // ##
        osMutexRelease(ring->head_mutex);
    }

    return return_size;
}

